/* What's specific for qstat */

module qstat;

private {
	import common;
	import parselist;
	import serverlist;
	import servertable;
	import main;
	import settings;
}

const char[] FIELDSEP = "\x1e"; // \x1e = ascii record separator


void parseOutput(void delegate(Object) callback, char[] delegate() readLine,
                 bool delegate() eof=null, char[] outputFileName=null)
{
	char[] line, s;
	char[][] server, fields, gtypes;
	BufferedFile outfile;
	Timer timer = new Timer;
	int count = 0;

	assert(callback);

	if (outputFileName) {
		try {
			outfile = new BufferedFile(outputFileName, FileMode.OutNew);
		}
		catch (OpenException e) {
			error("Unable to open \'" ~ outputFileName~ "\', the\n"
			      "server list will not be saved to disk.");
			outfile = null;
		}
	}

	scope (exit) {
		if (outfile)
			outfile.close();
		log("	qstat.parseOutput took " ~ std.string.toString(timer.doubleSecs) ~ " seconds.");
	}

	if (settings.modName in gameTypes)
		gtypes = gameTypes[settings.modName];
	else
		gtypes = defaultGameTypes;

	abort = false;

each_server:
	while (!abort && (!eof || !eof())) {
		ServerData sd;
		line = readLine();
		if (outfile)
			outfile.writeLine(line);
		if (line && line.length >= 3 && line[0..3] == "Q3S") {
			fields = split(line, FIELDSEP);
			assert(fields.length == 9 || fields.length == 3);
			count++;
			display.asyncExec(new Integer(count), callback);

			if (fields.length >= 9) {
				if (settings.modName != "baseq3" &&
				             MOD_ONLY && icmp(fields[8], settings.modName) != 0) {
					continue each_server;
				}

				// FIXME: use enum ServerColumn here instead
				sd.server ~= fields[2];        // name
				sd.server ~= fields[6];        // ping
				sd.server ~= "";               // players
				sd.server ~= "";               // gametype
				sd.server ~= fields[3];        // map
				sd.server ~= fields[1];        // IP

				// parse cvars
				line = readLine();
				if (outfile)
					outfile.writeLine(line);
				char[][] temp = split(line, FIELDSEP);
				foreach (char[] s; temp) {
					char[][] cvar = split(s, "=");
					switch (cvar[0]) {
						case "gametype":
							int gt = toInt(cvar[1]);
							if (gt < gtypes.length) {
								sd.server[ServerColumn.GAMETYPE] = gtypes[gt];
							}
							else {
								sd.server[ServerColumn.GAMETYPE] = std.string.toString(gt);
							}
							break;
						//case "game":
						case "gamename":
							if (MOD_ONLY && icmp(cvar[1], settings.modName) != 0) {
								continue each_server;
							}
							break;
						default:
							break;
					}
					sd.cvars ~= cvar;
				}
				sortStringArray(sd.cvars);

				// parse players
				int humans = 0;
				while ((line = readLine()) !is null && line.length) {
					if (outfile)
						outfile.writeLine(line);
					char[][] s = split(line, FIELDSEP);
					sd.players ~= s;
					if (s[PlayerColumn.PING] != "0")
						humans++;
				}

				// 'Players' column contents
				int bots = toInt(fields[5]) - humans;
				if (bots < 0)
					bots = 0;
				sd.server[ServerColumn.PLAYERS] = std.string.toString(humans) ~
				                  "+" ~ (std.string.toString(bots)) ~ "/" ~ fields[4];

				serverList.insertSorted(sd);
			}
			else /*if (!MOD_ONLY)*/ { // server didn't respond
				/*sd.server.length = servertable.serverHeaders.length;
				sd.server[ServerColumn.ADDRESS] = fields[1]; // ip
				serverList.insertSorted(sd);*/
			}
			if (outfile)
				outfile.writeLine("");
			//display.asyncExec(null, &serverTable.update);
		}
	}
	//display.asyncExec(null, &serverTable.update);
}


/** Save the server list so that qstat can refresh servers
 *
 * Throws: OpenException, maybe WriteException in rare cases
 */
void saveRefreshList()
{
	BufferedFile f = new BufferedFile(parselist.REFRESHFILE, FileMode.OutNew);
	scope(exit) f.close();

	foreach (ServerData sd; serverList) {
		f.writeLine(sd.server[ServerColumn.ADDRESS]);
	}
}

/** Count how many servers in the file qstat reads when it refreshes the
 *  server list.
 *
 *  Useful for getting the number of servers before qstat has started
 *  to retrieve them.
 *
 * Throws: OpenException, maybe WriteException in rare cases
 */
int countServersInRefreshList()
{
	int count = 0;
	BufferedFile f = new BufferedFile(parselist.REFRESHFILE);
	scope(exit) f.close();

	foreach (char[] line; f)
		count++;

	return count;
}
